using System;
using Coderoom.ScrumPlanner.Data;
using Coderoom.ScrumPlanner.Domain;
using Coderoom.ScrumPlanner.Tests.FakeRepositories;
using Xunit;

namespace Coderoom.ScrumPlanner.Tests.Unit.ServiceTests
{
	public class ProjectServiceTests
	{
		private readonly ProjectService _service;

		public ProjectServiceTests()
		{
			var repository = new FakeProjectRepository();
			_service = new ProjectService(repository);
		}

		[Fact]
		public void GetProjects_returns_ProjectCollection()
		{
			// act
			var projects = _service.GetProjects();

			// assert
			Assert.NotNull(projects);
			Assert.IsType<ProjectCollection>(projects);
			Assert.Equal(3, projects.Count);

			Assert.Equal(new Guid("CF4F74C1-CE66-4B06-8480-B34445906423"), projects[0].Id);
			Assert.Equal("Project A", projects[0].Title);
			Assert.Equal("project-a", projects[0].Slug);

			Assert.Equal(new Guid("D8185A6C-7145-4751-B149-27B03AB9E5B6"), projects[1].Id);
			Assert.Equal("Project B", projects[1].Title);
			Assert.Equal("project-b", projects[1].Slug);
		}

		[Fact]
		public void GetProject_by_id_returns_Project_when_project_is_found()
		{
			// arrange
			var id = _service.GetProjects()[1].Id;

			// act
			var project = _service.GetProject(id);

			// assert
			Assert.NotNull(project);
			Assert.IsType<Project>(project);
			Assert.Equal(id, project.Id);
			Assert.Equal("Project B", project.Title);
			Assert.Equal("project-b", project.Slug);
		}

		[Fact]
		public void GetProject_by_id_returns_null_when_project_is_not_found()
		{
			// arrange
			var id = Guid.Empty;

			// act
			var project = _service.GetProject(id);

			// assert
			Assert.Null(project);
		}

		[Fact]
		public void GetProject_by_slug_returns_Project_when_project_is_found()
		{
			// arrange
			var slug = _service.GetProjects()[1].Slug;

			// act
			var project = _service.GetProject(slug);

			// assert
			Assert.NotNull(project);
			Assert.IsType<Project>(project);
			Assert.Equal(new Guid("D8185A6C-7145-4751-B149-27B03AB9E5B6"), project.Id);
			Assert.Equal("Project B", project.Title);
			Assert.Equal(slug, project.Slug);
		}

		[Fact]
		public void GetProject_by_slug_returns_null_when_project_is_not_found()
		{
			// arrange
			var slug = "494EEF7D-8E08-418B-9A53-9B2349D193F0";

			// act
			var project = _service.GetProject(slug);

			// assert
			Assert.Null(project);
		}

		[Fact]
		public void GetProject_by_slug_throws_ArgumentNullException_when_called_with_null_slug()
		{
			// assert
			Assert.Throws<ArgumentNullException>(() => _service.GetProject(null));
		}

		[Fact]
		public void CreateProject_returns_OperationResult_true_when_project_is_created()
		{
			// arrange
			Assert.Equal(3, _service.GetProjects().Count);

			var newProject = new Project();
			newProject.Title = "Project C";
			newProject.Slug = "project-c";

			// act
			var result = _service.CreateProject(newProject);

			// assert
			Assert.IsType<OperationResult>(result);
			Assert.True(result.IsSuccessful);

			var projects = _service.GetProjects();
			Assert.Equal(4, projects.Count);

			Assert.NotEqual(Guid.Empty, projects[3].Id);
			Assert.Equal("Project C", projects[3].Title);
			Assert.Equal("project-c", projects[3].Slug);
		}

		[Fact]
		public void CreateProject_throws_ArgumentNullException_when_called_with_null_project()
		{
			// act
			Assert.Throws<ArgumentNullException>(() => _service.CreateProject(null));
		}

		[Fact]
		public void CreateProject_returns_false_when_Title_is_null()
		{
			// arrange
			Assert.Equal(3, _service.GetProjects().Count);

			var newProject = new Project();
			newProject.Title = null;
			newProject.Slug = "project-z";

			// act
			var result = _service.CreateProject(newProject);

			// assert
			Assert.False(result.IsSuccessful);
			Assert.Equal(1, result.ValidationErrors.Count);
			Assert.Equal("Title", result.ValidationErrors[0].PropertyName);
			Assert.Equal("Please enter a title.", result.ValidationErrors[0].Message);
			Assert.Equal(newProject, result.ValidationErrors[0].ValidationTarget);
			Assert.Equal(3, _service.GetProjects().Count);
		}

		[Fact]
		public void CreateProject_returns_false_when_Title_is_empty()
		{
			// arrange
			Assert.Equal(3, _service.GetProjects().Count);

			var newProject = new Project();
			newProject.Title = "";
			newProject.Slug = "project-z";

			// act
			var result = _service.CreateProject(newProject);

			// assert
			Assert.False(result.IsSuccessful);
			Assert.Equal(1, result.ValidationErrors.Count);
			Assert.Equal("Title", result.ValidationErrors[0].PropertyName);
			Assert.Equal("Please enter a title.", result.ValidationErrors[0].Message);
			Assert.Equal(newProject, result.ValidationErrors[0].ValidationTarget);
			Assert.Equal(3, _service.GetProjects().Count);
		}

		[Fact]
		public void CreateProject_returns_false_when_Slug_is_in_use()
		{
			// arrange
			Assert.Equal(3, _service.GetProjects().Count);

			var newProject = new Project();
			newProject.Title = "Project A";
			newProject.Slug = "project-a";

			// act
			var result = _service.CreateProject(newProject);

			// assert
			Assert.False(result.IsSuccessful);
			Assert.Equal(1, result.ValidationErrors.Count);
			Assert.Equal("Slug", result.ValidationErrors[0].PropertyName);
			Assert.Equal("The slug is already in use. Please enter a different slug.", result.ValidationErrors[0].Message);
			Assert.Equal(newProject, result.ValidationErrors[0].ValidationTarget);
			Assert.Equal(3, _service.GetProjects().Count);
		}

		[Fact]
		public void DeleteProject_by_id_returns_true_when_project_is_deleted()
		{
			// arrange
			Assert.Equal(3, _service.GetProjects().Count);

			var id = _service.GetProjects()[1].Id;

			// act
			var result = _service.DeleteProject(id);

			// assert
			Assert.True(result);
			Assert.Equal(2, _service.GetProjects().Count);
		}

		[Fact]
		public void DeleteProject_by_id_returns_false_when_project_is_not_deleted()
		{
			// arrange
			Assert.Equal(3, _service.GetProjects().Count);

			var id = Guid.Empty;

			// act
			var result = _service.DeleteProject(id);

			// assert
			Assert.False(result);
			Assert.Equal(3, _service.GetProjects().Count);
		}

		[Fact]
		public void DeleteProject_by_slug_returns_true_when_project_is_deleted()
		{
			// arrange
			Assert.Equal(3, _service.GetProjects().Count);

			var slug = _service.GetProjects()[1].Slug;

			// act
			var result = _service.DeleteProject(slug);

			// assert
			Assert.True(result);
			Assert.Equal(2, _service.GetProjects().Count);
		}

		[Fact]
		public void DeleteProject_by_slug_returns_false_when_project_is_not_deleted()
		{
			// arrange
			Assert.Equal(3, _service.GetProjects().Count);

			// act
			var result = _service.DeleteProject("");

			// assert
			Assert.False(result);
			Assert.Equal(3, _service.GetProjects().Count);
		}

		[Fact]
		public void DeleteProject_by_slug_throws_ArgumentNullException_when_called_with_null_slug()
		{
			// act
			Assert.Throws<ArgumentNullException>(() => _service.DeleteProject(null));
		}
	}
}